﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "ProfileUsedModelLineUtils.h"
#include "IModelLine.h"
#include "ModelLineUsedProfileData.h"
#include "ILine3d.h"
#include "IDocument.h"
#include "IGenericElement.h"
#include "IExternalDatas.h"
#include "ICurve2d.h"
#include "IElementParameters.h"
#include "ProfileUsedModelLineParametersCustomizer.h"
#include "IElementStatus.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

CREATE_DEBUG_MODE(SetModelLineNotCuttableByViewRange, L"用作轮廓线的模型线设置为不被视图范围裁剪", gcmp::DebugModeGroup::DMGT_VIEW, L"GDMPLab", L"2023-12-20", L"打开此调试模式会把用作轮廓线的模型线设置为不被视图范围裁剪。");

// 模型线用做轮廓线数据data
const std::wstring ED_KEY_MODEL_LINE_USED_PROFILE = L"ModelLineUsedProfileDataKey";

IModelLine* gcmp::ProfileUsedModelLineUtils::CreateModelLineUsedForProfile(IDocument* pDoc, OwnerPtr<ICurve3d> opCurve)
{
    // 基于三维曲线创建轮廓线
    IModelLine* pModelLine = IModelLine::Create(pDoc, TransferOwnership(opCurve));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelLine, L"pIModelLine创建失败",L"GDMPLab",L"2024-03-30");

    // 注册外部数据，标记轮廓线已经被使用
    pModelLine->GetExternalDatas()->RegisterExternalData(ED_KEY_MODEL_LINE_USED_PROFILE,
        NEW_AS_OWNER_PTR(ModelLineUsedProfileData, pDoc, pModelLine->GetOwnerElement()->GetElementId()));

    //定制轮廓线的参数行为
    IElement* pGenericElement = pModelLine->GetOwnerElement();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGenericElement, L"pGenericElement创建失败",L"GDMPLab",L"2024-03-30");

    if (pGenericElement->GetElementParameters())
        pGenericElement->GetElementParameters()->SetElementParametersCustomizer(NEW_AS_OWNER_PTR(ProfileUsedModelLineParametersCustomizer));

    if (DEBUG_MODE(SetModelLineNotCuttableByViewRange))
    {
        // 用作轮廓线的模型线设置为不被视图范围裁剪
        pModelLine->SetCuttableByModelViewRange(false);
    }

    return pModelLine;
}

IModelLine* gcmp::ProfileUsedModelLineUtils::CreateModelLineUsedForProfile(IDocument* pDoc, const ElementId& planeId, OwnerPtr<ICurve2d> opCurve)
{
    // 创建有关联平面的模型线
    IModelLine* pModelLine = IModelLine::Create(pDoc, planeId, TransferOwnership(opCurve));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelLine, L"pIModelLine创建失败",L"GDMPLab",L"2024-03-30");

    // 注册外部数据，标记轮廓线已经被使用
    pModelLine->GetExternalDatas()->RegisterExternalData(ED_KEY_MODEL_LINE_USED_PROFILE,
        NEW_AS_OWNER_PTR(ModelLineUsedProfileData, pDoc, pModelLine->GetOwnerElement()->GetElementId()));

    //定制轮廓线的参数行为
    IElement* pGenericElement = pModelLine->GetOwnerElement();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGenericElement, L"pGenericElement创建失败",L"GDMPLab",L"2024-03-30");

    if (pGenericElement->GetElementParameters())
        pGenericElement->GetElementParameters()->SetElementParametersCustomizer(NEW_AS_OWNER_PTR(ProfileUsedModelLineParametersCustomizer));

    if (DEBUG_MODE(SetModelLineNotCuttableByViewRange))
    {
        // 用作轮廓线的模型线设置为不被视图范围裁剪
        pModelLine->SetCuttableByModelViewRange(false);
    }

    return pModelLine;
}

bool gcmp::ProfileUsedModelLineUtils::IsModelLineUsedForProfile(const IModelLine* pIModelLine)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pIModelLine, L"pIModelLine为空",L"GDMPLab",L"2024-03-30");

    if (pIModelLine->GetExternalDatas()->GetAllExternalDatas().empty())
    {
        return false;
    }
    else
    {
        if (pIModelLine->GetExternalDatas()->FindExternalData(ED_KEY_MODEL_LINE_USED_PROFILE) != nullptr)
        {
            return true;
        }
        else 
        {
            return false;
        }
    }
}

bool ProfileUsedModelLineUtils::IsModelLineUsedForProfile(const IElement* pElement)
{
    const IGenericElement* pGenericElement = quick_cast<IGenericElement>(pElement);
    if (!pGenericElement)
    {
        return false;
    }

    const IModelLine* pModelLine = quick_cast<IModelLine>(pGenericElement->GetExternalObject());
    if (!pModelLine)
    {
        return false;
    }

    return IsModelLineUsedForProfile(pModelLine);
}

void gcmp::ProfileUsedModelLineUtils::SetAssociatedInstanceIdForModelLines(std::vector<IModelLine*> profileCurves, ElementId instanceId)
{
    for (auto& pIModelLine : profileCurves)
    {

        auto pData = pIModelLine->GetExternalDatas()->FindExternalData(ED_KEY_MODEL_LINE_USED_PROFILE);

        DBG_WARN_AND_RETURN_VOID_UNLESS(pData != nullptr, L"传入的轮廓线不为该instance轮廓",L"GDMPLab",L"2024-03-30");
        ModelLineUsedProfileData* pProfileData = quick_cast<ModelLineUsedProfileData>(pData);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pProfileData, L"pProfileData为空",L"GDMPLab",L"2024-03-30");
        pProfileData->SetInstnaceId(instanceId);
    }
}

bool gcmp::ProfileUsedModelLineUtils::GetStructureProfileModelLinesInCurrDoc(IDocument*pDoc, std::vector<IModelLine*> & Profiles)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    std::vector<IModelLine*> modelLines = IModelLine::GetAllModelLines(pDoc);
    FOR_EACH(pIModelLine, modelLines)
    {
        const IElement* pElement = pIModelLine->GetOwnerElement();
        if (!pElement->GetStatus()->IsVisible())
        {
            continue;
        }
        if (ProfileUsedModelLineUtils::IsModelLineUsedForProfile(pIModelLine))
        {
            Profiles.push_back(pIModelLine);
        }
    }
    return true;
}

bool gcmp::ProfileUsedModelLineUtils::GetModelLinesUsedForProfileByElementIds(IDocument* pDoc, const std::vector<ElementId>& elementIds,
    std::vector<IModelLine*> & profileCurves)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_FALSE_UNLESS(elementIds.size() > 0, L"elementIds为空",L"GDMPLab",L"2024-03-30");

    FOR_EACH(id, elementIds)
    {
        IElement* pProfileLineElement = pDoc->GetElement(id);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pProfileLineElement, L"pProfileLineElement为空",L"GDMPLab",L"2024-03-30");

        IGenericElement *pGenericElement = quick_cast<IGenericElement>(pProfileLineElement);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGenericElement, L"pGenericElement为空",L"GDMPLab",L"2024-03-30");

        IModelLine* pModelLine = quick_cast<IModelLine>(pGenericElement->GetExternalObject());
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelLine, L"pModelLine为空",L"GDMPLab",L"2024-03-30");

        if (ProfileUsedModelLineUtils::IsModelLineUsedForProfile(pModelLine))
        {
            profileCurves.push_back(pModelLine);
        }
    }
    DBG_WARN_AND_RETURN_FALSE_UNLESS(profileCurves.size() > 0, L"profileCurves为空",L"GDMPLab",L"2024-03-30");

    return true;
}

bool gcmp::ProfileUsedModelLineUtils::GetInstanceAllProfliesInCurrDoc(IDocument*pDoc, const ElementId& instanceId, std::vector<IModelLine*> & Profiles)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    std::vector<IModelLine*> modelLines = IModelLine::GetAllModelLines(pDoc);
    FOR_EACH(pIModelLine, modelLines)
    {
        auto pData = pIModelLine->GetExternalDatas()->FindExternalData(ED_KEY_MODEL_LINE_USED_PROFILE);

        if (pData != nullptr)
        {
            const ModelLineUsedProfileData* ptrData = quick_cast<const ModelLineUsedProfileData>(pData);
            if (ptrData->GetInstanceId() == instanceId)
            {
                Profiles.push_back(pIModelLine);
            }
        }
    }
    return true;
}
